use std::{collections::HashMap, sync::Arc};

use monitor::{CompleterSuggestion, Monitor, MonitorCommand};

use super::{fdinfo::fdinfo_insert, mon_command_completer};
use crate::{DfsError, DfsOpenOptions, DfsResult};

struct OpenCommand;

impl OpenCommand {
    fn do_open(&self, args: &HashMap<String, String>) -> DfsResult<u64> {
        let file = args.get("file").unwrap();
        let attr = args.get("attr").unwrap().as_bytes();
        if attr.len() != 5 {
            return Err(DfsError::InvalidArgs);
        }
        let read = attr[0] == b'r';
        let write = attr[1] == b'w';
        let seek = attr[2] == b's';
        let fcntl = attr[3] == b'f';
        let block = attr[4] == b'b';
        let res = DfsOpenOptions::new()
            .read(read)
            .write(write)
            .seek(seek)
            .fcntl(fcntl)
            .noblock(!block)
            .open(file)?;

        let id = res.id();
        fdinfo_insert(res)?;
        Ok(id)
    }
}

impl MonitorCommand for OpenCommand {
    fn mon_protocol_exec(&self, args: &monitor::Protocol) -> DfsResult<monitor::Protocol> {
        let id = self.do_open(args)?;
        let mut hash = HashMap::new();
        hash.insert("fd".to_string(), id.to_string());
        Ok(hash)
    }

    fn mon_readline_arg_type(&self) -> Option<&'static str> {
        Some("file:*,attr:str")
    }

    fn mon_readline_completer(&self, args: &[&str], is_space: bool) -> Option<CompleterSuggestion> {
        mon_command_completer(args.last(), false, is_space)
    }
    fn mon_readline_exec(
        &self,
        args: &HashMap<String, String>,
        printer: &monitor::MonitorPrinter,
    ) -> DfsResult<()> {
        let id = self.do_open(args)?;
        printer.print(id.to_string());
        Ok(())
    }

    fn mon_readline_help(&self) -> String {
        String::from("打开一个文件")
    }

    fn mon_readline_long_help(&self) -> Option<String> {
        Some(String::from("open <file> <rwsfb>   - <rwsfb>打开属性, 不需要的设置为-"))
    }
}

pub(crate) fn monitor_command_open_register(mon: &Monitor) {
    mon.register_command("open", Arc::new(OpenCommand)).unwrap();
}
